/*
 * Copyright (c) 2008, Artur Emagulov
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * 23.08.2008
 * main.cpp
 */

#include <platformjack.h>
#include <multiboot.h>
#include <kiddie/kiddie.h>
#include <sys/types.h>

#include <Console.h>
#include <hw/Platform.h>
#include <sys/sched/Scheduler.h>

#include <asm-syscalls.h>

#include <virt/Jvm.h>
#include <kiddie/IRunnable.h>

using util::IRunnable;

/* gcc workaround */
extern "C" void* __dso_handle;
extern "C" void
__aeabi_atexit(void)
{
}
extern "C" void
__cxa_atexit(void)
{
}
extern "C" void
__cxa_pure_virtual(void)
{
}
void* __dso_handle = NULL;

/** 'Platfrom' singleton */
kernel::Platform hwplatform;
/** System console singleton */
kernel::Console cout = ::hwplatform.console;

/**
 * Task lists
 * @todo move this into global resource database (ResourceDb)
 * @todo associate tasks with Cpu they are running on
 */
util::list<kernel::sys::sched::task::Task *> running;
util::list<kernel::sys::sched::task::Task *> finished;

kernel::sys::sched::task::Task *trash;
kernel::sys::sched::task::Task *current;
kernel::sys::sched::task::Task *k_task; // kernel main task

/**
 * @todo rename 'initcalls' as it must do resource lookup not just init.
 */
void
kernel::initcalls(void *data)
{
  module_call_t *_func;
  int *func;
  util::Capabilities _caps;
  util::IRunnable *qqq = 0;

  ::cout << kernel::Console::HEX << "[ kernel::initcalls() ] arg1=0x"
      << (int) data << "\n";

  for (_func = (module_call_t *) &__early_initcall_end, func
      = (int *) &__early_initcall_end; _func
      < (module_call_t *) &__initcall_end; func++, _func
      = (module_call_t *) func)
    {
      ::cout << (char *) "[ kernel::initcalls() ] func(0x" << (int) _func
          << (char *) ")\r\n";

      qqq = (*_func)(_caps);
      if (qqq != NULL)
        {
          _caps = qqq->getCaps();
          ::cout << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%   " << _caps.tags << "\r\n"
              << kernel::Console::DEC;
          ::hwplatform.eventController[kernel::irq::Event::KeyboardEvent] = qqq;
        }

    }

  return;
}

/**
 *
 */
void
test_thread_3(void *data)
{
  int i;
  kernel::sys::sched::task::Task *t;
#ifdef X86
  multiboot_info_t *mbi;
  module_t *mod;

  struct hw_profile *hwpp =
  ::hwplatform.bootInfo;

  mbi = (multiboot_info_t *)
  phys_to_virt(hwpp->mbi);

  for (i = 0, mod = (module_t *) phys_to_virt(mbi->mods_addr);
      i < mbi->mods_count;
      i++, mod++)
    {

#if 1
      kernel::virt::jvm::Jvm *qqvm =
      new kernel::virt::jvm::Jvm(
          (char *)phys_to_virt(mod->mod_start));
      ::cout << " test_thread_3:  ^^ " << i << "\n";
      qqvm->runAtoms(0);
#else
      t = ::hwplatform.taskFactory.taskFromMemory((vaddr_t)phys_to_virt(mod->mod_start));
      *::hwplatform.scheduler += t;
      ::cout << kernel::Console::HEX << " test_thread_3:  t->entry=0x" << (int)t->entry << "\n" << kernel::Console::DEC;
#endif
    }
#endif

  for (;;)
    {
    }
  return;

}

/*
 * The main kernel routine
 * @param addr pointer to hardware platform jack
 *
 */
extern "C" void
kmain(int __mm, struct hw_profile *addr)
{
  char *cmdline;
  kernel::sys::sched::task::Task *qts;

  /* get virtual address of hardware profile
   * passed from early kernel loader
   */
  addr = (struct hw_profile *) phys_to_virt((void *) addr);

  // Print out greetings.
  ::cout << "Kiddie-OS (c) 2009, Exo [" << (int) __mm << ":" << (int) addr
      << "]\n";

  ::cout << "CPU Vendor ID: " << addr->cpuid.vendor << "\n";
  ::cout << "Stepping: " << (int) addr->cpuid.features.f_stepping << "\n";
  ::cout << "Model: " << (int) addr->cpuid.features.f_model << "\n";
  ::cout << "Family: " << (int) addr->cpuid.features.f_family << "\n";
  ::cout << "Type: " << (int) addr->cpuid.features.f_type << "\n";

  // Do scan/initialize hardware
  ::hwplatform.init(addr);

  /* create new scheduler */
  ::hwplatform.scheduler = new kernel::sys::sched::Scheduler();
  *::hwplatform.scheduler << kernel::cmd::STOP;

  /* create kernel idle task.
   * this task will be used by scheduler. */
  ::current = ::k_task = ::hwplatform.taskFactory.newTask(
      kernel::sys::sched::task::Task::KERNEL);
  ::running.add(::k_task);

  /* create trash task to be used as trash for deleted tasks */
  ::trash = ::hwplatform.taskFactory.newTask(
      kernel::sys::sched::task::Task::KERNEL);

  if (!::hwplatform.scheduler)
    {
      ::cout << "[ PANIC!!! ] Can't create Scheduler!\n";
    }
  else
    {

      qts = ::hwplatform.taskFactory.newTask(
          (kernel::sys::sched::task::Task::task_entry_t) test_thread_3,
          ::kernel::sys::sched::task::Task::KERNEL);

      /* add newly created task to scheduler */
      *::hwplatform.scheduler += qts;

//      qts = ::hwplatform.taskFactory.newTask(
//          (kernel::sys::sched::task::Task::task_entry_t) ::kernel::initcalls,
//          ::kernel::sys::sched::task::Task::KERNEL);
//
//      /* add newly created task to scheduler */
//      *::hwplatform.scheduler += qts;
    }

  /* start task scheduling */
  *::hwplatform.scheduler << kernel::cmd::START;

  /* register 'scheduler' as 'timerEvent' receiver and turn on
   * the event controller */
  ::hwplatform.eventController << kernel::irq::EventController::OFF;
  ::hwplatform.eventController[kernel::irq::Event::TimerEvent]
      = ::hwplatform.scheduler;
  ::hwplatform.eventController << kernel::irq::EventController::ON;

  /* do power safe loop in main thread */
  for (;;)
    {
      ::hwplatform.idle();
    }

  return;
}
